﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
using System.Xml.Linq;

namespace Util
{
    public static class ObjectExtension
    {
        /// <summary>
        /// 转成友好字符串
        /// </summary>
        /// <param name="current">当前时间</param>
        /// <returns>友好的时间展示</returns>
        public static string ToFriendlyString(this DateTime current)
        {
            var now = DateTime.Now;
            var ts = now - current;
            if (ts.Days >= 365)
                return (ts.Days / 365) + "年前";
            else if (ts.Days >= 30)
                return (ts.Days / 30) + "个月前";
            else if (ts.Days > 0)
                return ts.Days + "天前";
            else if (ts.Hours > 0)
                return ts.Hours + "小时前";
            else if (ts.Minutes > 0)
                return ts.Minutes + "分钟前";
            else
                return "刚刚";
        }

        /// <summary>
        /// 当年之初
        /// </summary>
        /// <param name="current"></param>
        /// <returns></returns>
        public static DateTime Year(this DateTime current)
        {
            return new DateTime(current.Year, 1, 1);
        }

        /// <summary>
        /// 当月之初
        /// </summary>
        /// <param name="current"></param>
        /// <returns></returns>
        public static DateTime Month(this DateTime current)
        {
            return new DateTime(current.Year, current.Month, 1);
        }

        /// <summary>
        ///     将对象转换为指定格式的字符串
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static string ToJson(this Object obj)
        {
            return JsonHelper.ToJson(obj);
        }

        /// <summary>
        ///  将手机号转换为打码格式*******5006格式
        /// </summary>
        /// <param name="str">手机号</param>
        /// <returns></returns>
        public static string ToMaskPhone(this string str)
        {
            #region 打码格式152********
            //var phone = str + "";
            //if (phone.Length > 3)
            //{
            //	var starts = phone.Substring(0, 3);
            //	var ends = string.Empty;
            //	for (var i = 0; i < phone.Length - 3; i++)
            //		ends += "*";
            //	return starts + ends;
            //}
            //return phone;
            #endregion

            //打码格式*******5006
            var phone = str + "";
            var phoneLength = phone.Length;
            if (phoneLength > 4)
            {
                var ends = phone.Substring(phoneLength - 4);
                var starts = string.Empty;
                for (var i = 0; i < phoneLength - 4; i++)
                    starts += "*";
                return starts + ends;
            }
            return phone;
        }

        /// <summary>
        ///  将银行号转换为打码格式6224********5006格式
        /// </summary>
        /// <param name="str">卡号</param>
        /// <returns></returns>
        public static string ToMaskBankCardNo(this string str)
        {
            var bankCardNo = str + "";
            var bankCardNoLength = bankCardNo.Length;
            if (bankCardNoLength > 8)
            {
                var starts = bankCardNo.Substring(0, 4);
                var num = bankCardNo.Substring(4, bankCardNoLength - 4).Length;
                var ends = bankCardNo.Substring(num, 4);
                var middle = string.Empty;
                for (var i = 0; i < num - 4; i++)
                    middle += "*";
                return starts + middle + ends;
            }
            return bankCardNo;
        }

        /// <summary>
        /// 去除html标签
        /// </summary>
        /// <param name="str">当前字符串</param>
        /// <returns>去除html的字符串</returns>
        public static string ClearHtml(this string str)
        {
            return Regex.Replace(str ?? "", @"<[^>]*>", String.Empty);
        }

        /// <summary>
        /// 截取字符串
        /// </summary>
        /// <param name="str">当前字符串</param>
        /// <param name="len"></param>
        /// <param name="symbol">符号</param>
        /// <returns></returns>
        public static string Cut(this string str, int len, string symbol = "..")
        {
            if (str != null)
            {
                if (str.Length <= len)
                    return str;
                else
                    return str.Substring(0, len) + symbol;
            }
            return string.Empty;
        }

        /// <summary>
        /// 将对象属性转换为key-value对
        /// </summary>
        /// <param name="o"></param>
        /// <returns></returns>
        public static Dictionary<String, Object> ToMap(this Object o)
        {
            var map = new Dictionary<string, object>();
            try
            {
                var t = o.GetType();
                var pi = t.GetProperties(BindingFlags.Public | BindingFlags.Instance);

                foreach (PropertyInfo p in pi)
                {
                    MethodInfo mi = p.GetGetMethod();
                    if (mi != null && mi.IsPublic)
                    {
                        map.Add(p.Name, mi.Invoke(o, new Object[] { }));
                    }
                }
            }
            catch
            { }
            return map;
        }

        /// <summary>
        ///     将对象转换为指定格式的字符串
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static string ToFormatString(this Object obj)
        {
            //定义一个List集合，将各个实体类型存起来
            var models = obj.GetType();
            //得到公共属性集合
            var productFields = models.GetProperties();

            StringBuilder str = new StringBuilder();

            //得到属性上的标记属性信息即列名   
            foreach (PropertyInfo pi in productFields)
            {
                if (pi.GetValue(obj, null) != null)
                {
                    DescriptionAttribute desc = (DescriptionAttribute)Attribute.GetCustomAttribute(pi, typeof(Attribute));
                    if (desc != null)
                    {
                        str.AppendFormat("|{0}:{1}", desc.Description ?? string.Empty, pi.GetValue(obj, null));
                    }
                }
            }
            //移除第一个竖杠
            if (str.Length > 0)
            { str.Remove(0, 1); }
            return str.ToString();
        }

        /// <summary>
        ///     转换为short，默认值：short.MinValue
        /// </summary>
        /// <param name="strText"></param>
        /// <returns></returns>
        public static short TryShort(this string strText)
        {
            return TryShort(strText, short.MinValue);
        }

        /// <summary>
        ///     转换为short
        /// </summary>
        /// <param name="strText"></param>
        /// <param name="defValue"></param>
        /// <returns></returns>
        public static short TryShort(this string strText, short defValue)
        {
            return short.TryParse(strText + "", out short result) ? result : defValue;
        }

        /// <summary>
        ///     转换为short
        /// </summary>
        /// <param name="strText"></param>
        /// <param name="defValue"></param>
        /// <returns></returns>
        public static short? TryShort(this string strText, short? defValue)
        {
            return short.TryParse(strText + "", out short result) ? result : defValue;
        }

        /// <summary>
        ///     转换为Int，默认值：int.MinValue
        /// </summary>
        public static int TryInt(this string strText)
        {
            return TryInt(strText, int.MinValue);
        }

        /// <summary>
        ///     转换为Int
        /// </summary>
        /// <param name="strText"></param>
        /// <param name="defValue">默认值</param>
        /// <returns></returns>
        public static int TryInt(this string strText, int defValue)
        {
            return int.TryParse(strText + "", out int temp) ? temp : defValue;
        }

        /// <summary>
        ///     转换为Int
        /// </summary>
        /// <param name="strText"></param>
        /// <param name="defValue">默认值</param>
        /// <returns></returns>
        public static int? TryInt(this string strText, int? defValue)
        {
            return int.TryParse(strText + "", out int temp) ? temp : defValue;
        }

        /// <summary>
        ///     转换为Double，默认值：double.MinValue
        /// </summary>
        public static double TryDouble(this string strText)
        {
            return TryDouble(strText, double.MinValue);
        }

        /// <summary>
        ///     转换为Double
        /// </summary>
        /// <param name="strText"></param>
        /// <param name="defValue">默认值</param>
        /// <returns></returns>
        public static double TryDouble(this string strText, double defValue)
        {
            return double.TryParse(strText + "", out double temp) ? temp : defValue;
        }

        /// <summary>
        ///     转换为Double
        /// </summary>
        /// <param name="strText"></param>
        /// <param name="defValue">默认值</param>
        /// <returns></returns>
        public static double? TryDouble(this string strText, double? defValue)
        {
            return double.TryParse(strText + "", out double temp) ? temp : defValue;
        }

        /// <summary>
        ///     转换为Decimal，默认值：decimal.MinValue
        /// </summary>
        public static decimal TryDecimal(this string strText)
        {
            return TryDecimal(strText, decimal.MinValue);
        }

        /// <summary>
        ///     转换为Decimal
        /// </summary>
        /// <param name="strText"></param>
        /// <param name="defValue">默认值</param>
        /// <returns></returns>
        public static decimal TryDecimal(this string strText, decimal defValue)
        {
            return decimal.TryParse(strText + "", out decimal temp) ? temp : defValue;
        }

        /// <summary>
        ///     转换为Decimal
        /// </summary>
        /// <param name="strText"></param>
        /// <param name="defValue">默认值</param>
        /// <returns></returns>
        public static decimal? TryDecimal(this string strText, decimal? defValue)
        {
            return decimal.TryParse(strText + "", out decimal temp) ? temp : defValue;
        }

        /// <summary>
        ///     转换为long，默认值：long.MinValue
        /// </summary>
        public static long TryLong(this string strText)
        {
            return TryLong(strText, long.MinValue);
        }

        /// <summary>
        ///     转换为long
        /// </summary>
        /// <param name="strText"></param>
        /// <param name="defValue">默认值</param>
        /// <returns></returns>
        public static long TryLong(this string strText, long defValue)
        {
            return long.TryParse(strText + "", out long temp) ? temp : defValue;
        }

        /// <summary>
        ///     转换为long
        /// </summary>
        /// <param name="strText"></param>
        /// <param name="defValue">默认值</param>
        /// <returns></returns>
        public static long? TryLong(this string strText, long? defValue)
        {
            return long.TryParse(strText + "", out long temp) ? temp : defValue;
        }

        /// <summary>
        ///     转换为Boolean，默认值：false
        /// </summary>
        public static Boolean TryBool(this string strText)
        {
            return TryBool(strText, false);
        }

        /// <summary>
        ///     转换为Boolean
        /// </summary>
        /// <param name="strText"></param>
        /// <param name="defValue">默认值</param>
        /// <returns></returns>
        public static Boolean TryBool(this string strText, bool defValue)
        {
            if (strText.TryInt(0) == 1 || strText.TryString().ToUpper() == "TRUE")
                return true;
            return bool.TryParse(strText + "", out bool temp) ? temp : defValue;
        }

        /// <summary>
        ///     转换为Boolean
        /// </summary>
        /// <param name="strText"></param>
        /// <param name="defValue">默认值</param>
        /// <returns></returns>
        public static Boolean? TryBool(this string strText, bool? defValue)
        {
            var i = strText.TryInt(null);
            if (i.HasValue)
                return i == 1;
            return bool.TryParse(strText + "", out bool temp) ? temp : defValue;
        }

        /// <summary>
        ///     转换为DateTime，默认值：DateTimeExtension.DBNull
        /// </summary>
        public static DateTime TryDateTime(this string strText)
        {
            return TryDateTime(strText, new DateTime(1970, 1, 1));
        }

        /// <summary>
        ///     转换为DateTime
        /// </summary>
        /// <param name="strText"></param>
        /// <param name="defValue">默认值</param>
        /// <returns></returns>
        public static DateTime TryDateTime(this string strText, DateTime defValue)
        {
            //yyyy-MM-dd HH:mm
            //yyyy-M-d HH:mm
            //\d{4}\-\d{1,2}\-\d{1,2}( \d{1,2}:\d{2})?
            var timeText = strText.ToString().Trim();
            Regex re = new Regex(@"^\d{4}\-\d{1,2}\-\d{1,2}[ \+]\d{1,2}:\d{2}?$");
            if (re.IsMatch(timeText))
            {
                timeText = string.Format("{0}:00", timeText).Replace("+", " ");
            }

            if (DateTime.TryParse(timeText, out var temp))
                return temp;
            return defValue;
        }

        /// <summary>
        ///     转换为DateTime
        /// </summary>
        /// <param name="strText"></param>
        /// <param name="defValue">默认值</param>
        /// <returns></returns>
        public static DateTime? TryDateTime(this string strText, DateTime? defValue)
        {
            return DateTime.TryParse(strText + "", out var temp) ? temp : defValue;
        }

        /// <summary>
        ///     将对象转换为指定格式的字符串
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static string ToString(this Object obj)
        {
            //定义一个List集合，将各个实体类型存起来
            var models = obj.GetType();
            //得到公共属性集合
            var productFields = models.GetProperties();

            StringBuilder str = new StringBuilder();

            //得到属性上的标记属性信息即列名   
            foreach (PropertyInfo pi in productFields)
            {
                if (pi.GetValue(obj, null) != null)
                {
                    DescriptionAttribute desc = (DescriptionAttribute)Attribute.GetCustomAttribute(pi, typeof(Attribute));
                    if (desc != null)
                    {
                        str.AppendFormat("|{0}:{1}", desc.Description ?? string.Empty, pi.GetValue(obj, null));
                    }
                }
            }
            //移除第一个竖杠
            if (str.Length > 0)
            { str.Remove(0, 1); }
            return str.ToString();
        }

        /// <summary>
        ///     转换为short，默认值：short.MinValue
        /// </summary>
        /// <param name="strText"></param>
        /// <returns></returns>
        public static short TryShort(this Object strText)
        {
            return TryShort(strText, short.MinValue);
        }

        /// <summary>
        ///     转换为short
        /// </summary>
        /// <param name="strText"></param>
        /// <param name="defValue"></param>
        /// <returns></returns>
        public static short TryShort(this Object strText, short defValue)
        {
            short result;
            return short.TryParse(strText + "", out result) ? result : defValue;
        }

        /// <summary>
        ///     转换为short
        /// </summary>
        /// <param name="strText"></param>
        /// <param name="defValue"></param>
        /// <returns></returns>
        public static short? TryShort(this Object strText, short? defValue)
        {
            short result;
            return short.TryParse(strText + "", out result) ? result : defValue;
        }

        /// <summary>
        ///     转换为Int，默认值：int.MinValue
        /// </summary>
        public static int TryInt(this Object strText)
        {
            return TryInt(strText, int.MinValue);
        }

        /// <summary>
        ///     转换为Int
        /// </summary>
        /// <param name="strText"></param>
        /// <param name="defValue">默认值</param>
        /// <returns></returns>
        public static int TryInt(this Object strText, int defValue)
        {
            int temp;
            return int.TryParse(strText + "", out temp) ? temp : defValue;
        }

        /// <summary>
        ///     转换为Int
        /// </summary>
        /// <param name="strText"></param>
        /// <param name="defValue">默认值</param>
        /// <returns></returns>
        public static int? TryInt(this Object strText, int? defValue)
        {
            int temp;
            return int.TryParse(strText + "", out temp) ? temp : defValue;
        }

        /// <summary>
        ///     转换为Double，默认值：double.MinValue
        /// </summary>
        public static double TryDouble(this Object strText)
        {
            return TryDouble(strText, double.MinValue);
        }

        /// <summary>
        ///     转换为Double
        /// </summary>
        /// <param name="strText"></param>
        /// <param name="defValue">默认值</param>
        /// <returns></returns>
        public static double TryDouble(this Object strText, double defValue)
        {
            double temp;
            return double.TryParse(strText + "", out temp) ? temp : defValue;
        }

        /// <summary>
        ///     转换为Double
        /// </summary>
        /// <param name="strText"></param>
        /// <param name="defValue">默认值</param>
        /// <returns></returns>
        public static double? TryDouble(this Object strText, double? defValue)
        {
            double temp;
            return double.TryParse(strText + "", out temp) ? temp : defValue;
        }

        /// <summary>
        ///     转换为Decimal，默认值：decimal.MinValue
        /// </summary>
        public static decimal TryDecimal(this Object strText)
        {
            return TryDecimal(strText, decimal.MinValue);
        }

        /// <summary>
        ///     转换为Decimal
        /// </summary>
        /// <param name="strText"></param>
        /// <param name="defValue">默认值</param>
        /// <returns></returns>
        public static decimal TryDecimal(this Object strText, decimal defValue)
        {
            decimal temp;
            return decimal.TryParse(strText + "", out temp) ? temp : defValue;
        }

        /// <summary>
        ///     转换为Decimal
        /// </summary>
        /// <param name="strText"></param>
        /// <param name="defValue">默认值</param>
        /// <returns></returns>
        public static decimal? TryDecimal(this Object strText, decimal? defValue)
        {
            decimal temp;
            return decimal.TryParse(strText + "", out temp) ? temp : defValue;
        }

        /// <summary>
        ///     转换为long，默认值：long.MinValue
        /// </summary>
        public static long TryLong(this Object strText)
        {
            return TryLong(strText, long.MinValue);
        }

        /// <summary>
        ///     转换为long
        /// </summary>
        /// <param name="strText"></param>
        /// <param name="defValue">默认值</param>
        /// <returns></returns>
        public static long TryLong(this Object strText, long defValue)
        {
            long temp;
            return long.TryParse(strText + "", out temp) ? temp : defValue;
        }

        /// <summary>
        ///     转换为long
        /// </summary>
        /// <param name="strText"></param>
        /// <param name="defValue">默认值</param>
        /// <returns></returns>
        public static long? TryLong(this Object strText, long? defValue)
        {
            long temp;
            return long.TryParse(strText + "", out temp) ? temp : defValue;
        }

        /// <summary>
        ///     转换为Boolean，默认值：false
        /// </summary>
        public static Boolean TryBool(this Object strText)
        {
            return TryBool(strText, false);
        }

        /// <summary>
        ///     转换为Boolean
        /// </summary>
        /// <param name="strText"></param>
        /// <param name="defValue">默认值</param>
        /// <returns></returns>
        public static Boolean TryBool(this Object strText, bool defValue)
        {
            if (strText.TryInt(0) == 1)
                return true;
            bool temp;
            return bool.TryParse(strText + "", out temp) ? temp : defValue;
        }

        /// <summary>
        ///     转换为Boolean
        /// </summary>
        /// <param name="strText"></param>
        /// <param name="defValue">默认值</param>
        /// <returns></returns>
        public static Boolean? TryBool(this Object strText, bool? defValue)
        {
            var i = strText.TryInt(null);
            if (i.HasValue)
                return i == 1;
            bool temp;
            return bool.TryParse(strText + "", out temp) ? temp : defValue;
        }

        /// <summary>
        ///     转换为DateTime，默认值：DateTimeExtension.DBNull
        /// </summary>
        public static DateTime TryDateTime(this Object strText)
        {
            return TryDateTime(strText, new DateTime(1970, 1, 1));
        }

        /// <summary>
        ///     转换为DateTime
        /// </summary>
        /// <param name="strText"></param>
        /// <param name="defValue">默认值</param>
        /// <returns></returns>
        public static DateTime TryDateTime(this Object strText, DateTime defValue)
        {
            //yyyy-MM-dd HH:mm
            //yyyy-M-d HH:mm
            //\d{4}\-\d{1,2}\-\d{1,2}( \d{1,2}:\d{2})?
            var timeText = strText.ToString().Trim();
            Regex re = new Regex(@"^\d{4}\-\d{1,2}\-\d{1,2}[ \+]\d{1,2}:\d{2}?$");
            if (re.IsMatch(timeText))
            {
                timeText = string.Format("{0}:00", timeText).Replace("+", " ");
            }
            DateTime temp;
            if (DateTime.TryParse(timeText, out temp))
                return temp;
            return defValue;
        }

        /// <summary>
        ///     转换为DateTime
        /// </summary>
        /// <param name="strText"></param>
        /// <param name="defValue">默认值</param>
        /// <returns></returns>
        public static DateTime? TryDateTime(this Object strText, DateTime? defValue)
        {
            DateTime temp;
            return DateTime.TryParse(strText + "", out temp) ? temp : defValue;
        }

        /// <summary>
        ///     为NULL 和 DBNull的返回String.Empty
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static string TryString(this Object str)
        {
            return TryString(str, "");
        }

        /// <summary>
        ///     转换为""
        /// </summary>
        /// <param name="str"></param>
        /// <param name="defvalue"></param>
        /// <returns></returns>
        public static string TryString(this Object str, string defvalue = "")
        {
            return str?.ToString() ?? defvalue;
        }

        /// <summary>
        /// 转化字符串编码
        /// </summary>
        /// <param name="str">字符串</param>
        /// <param name="srcEncodeName">源编码名称</param>
        /// <param name="dstEncodeName">目标编码名称</param>
        /// <returns></returns>
        public static string Encode(this string str, string srcEncodeName, string dstEncodeName)
        {
            Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
            var srcEncoding = Encoding.GetEncoding(srcEncodeName);
            var dstEncoding = Encoding.GetEncoding(dstEncodeName);
            byte[] srcBytes = srcEncoding.GetBytes(str);
            byte[] bytes = Encoding.Convert(srcEncoding, dstEncoding, srcBytes);
            return dstEncoding.GetString(bytes);
        }

        /// <summary>
        ///     转换十六进制
        /// </summary>
        /// <param name="i"></param>
        /// <returns></returns>
        public static string ConvertX8(this int i)
        {
            return Convert.ToString(i, 16);
        }

        /// <summary>
        /// Pascal 命名方法的字符串
        /// </summary>
        /// <param name="str">字符串</param>
        /// <returns></returns>
        public static string ToPascal(this string str)
        {
            if (string.IsNullOrEmpty(str))
            {
                return "";
            }
            else
            {
                return str.Substring(0, 1).ToUpper() +
                               str.Substring(1, str.Length - 1);
            }
        }

        /// <summary>
        /// Camel 命名方法的字符串
        /// </summary>
        /// <param name="str">字符串</param>
        /// <returns></returns>
        public static string ToCamel(this string str)
        {
            if (string.IsNullOrEmpty(str))
            {
                return "";
            }
            else
            {
                return str.Substring(0, 1).ToLower() +
                               str.Substring(1, str.Length - 1);
            }
        }

        /// <summary>
        /// 当前索引
        /// </summary>
        /// <param name="arr">数组</param>
        /// <param name="obj">对象</param>
        /// <returns>返回当前索引</returns>
        public static int IndexOf(this string[] arr, string obj)
        {
            var index = -1;
            for (int i = 0; i < arr.Length; i++)
            {
                if (arr[i] == obj)
                {
                    index = -1;
                }
            }
            return index;
        }

        /// <summary>
        /// 获取异常的根异常
        /// </summary>
        /// <param name="ex"></param>
        /// <returns></returns>
        public static Exception GetRootException(this Exception ex)
        {
            if (ex == null)
                return null;
            if (ex.InnerException == null)
                return ex;
            else
            {
                return GetRootException(ex.InnerException);
            }
        }
        /// <summary>
        /// 把转换成字节
        /// </summary>
        /// <param name="stream"></param>
        /// <returns></returns>
        public static byte[] ToBytes(this Stream stream)
        {
            var bytes = new byte[stream.Length];
            stream.Read(bytes, 0, bytes.Length);
            // 设置当前流的位置为流的开始 
            stream.Seek(0, SeekOrigin.Begin);
            return bytes;
        }
        public static XDocument ToXDocument(this XmlDocument document)
        {
            return document.ToXDocument(LoadOptions.None);
        }
        public static XDocument ToXDocument(this XmlDocument document, LoadOptions options)
        {
            using (XmlNodeReader reader = new XmlNodeReader(document))
            {
                return XDocument.Load(reader, options);
            }
        }
    }
}
